package com.AdvancedDemos.FileAndIO;

import java.io.IOException;
import java.io.Serializable;

public class ObjectStream对象操作流 {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        //ObjectStream:对象操作流

        /*
        数据流向分类:
        ObjectInputStream:对象操作输入流,读取对象
        读取字节数据,转换为对象 -- 反序列化(有序的字节流变成无序的对象)
        ObjectOutputStream:对象操作输出流,写出对象
        将对象转换为字节数据,进行写出 -- 序列化(无序的对象变成有序的字节流)
        */

        /*想要一个类能够被序列化操作,必须要实现Serializable,Serializable接口中没有抽象方法,该接口作为标记性接口
         * 实现该接口的类的对象可以被序列化和反序列化操作*/

        //准备工作:
        //1.定义类,封装属性和行为
        //2.实现Serializalbe接口
        //3.创建对象输出流
        //4.writeObject(对象)
        //5.关流

        //对象操作输出流,写出对象ObjectOutputStream -- 序列化
        /*ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("helloworld\\src\\com\\AdvancedDemos\\FileAndIO\\objectStream.txt"));
        User user = new User("张三", 18);
        oos.writeObject(user);//将对象以字节的形式写出到文件中,肉眼不可读
        oos.close();*/

//------------------------------------------------------
        //对象操作输入流ObjectInputStream -- 反序列化
        /*ObjectInputStream ois = new ObjectInputStream(new FileInputStream("helloworld\\src\\com\\AdvancedDemos\\FileAndIO\\objectStream.txt"));
        //读取对象
        Object o = ois.readObject();//从文件中以字节的形式读取并转化成对象,返回值类型是Object
        System.out.println(o);//User{name='张三', age=18}  由于序列化存储时,文件内容是按照一定格式存储的,所以这里尽管o是Object类型,但是打印出的内容却是User类型的属性值

        if (o instanceof User) {//如果要调用User中特有的函数,需要强转
            ((User) o).method();//成员方法method
        }
        ois.close();*/


        /**
         * serialVersionUID是在序列化和反序列化操作过程中根据类中内容计算出来的long数据值
         *
         * 如果没有手动给出serialVersionUID,系统会自动根据类中内容进行计算
         *
         * 读取操作之前,在类中进行修改,会导致serialVersionUID重新计算
         * 所以如果自定义类对象存入文件后,改变了自定义类中的内容(如增加了一个成员变量),serialVersionUID会重新进行计算,写出的serialVersionUID是当前类中内容计算得到的
         *
         * Exception in thread "main" java.io.InvalidClassException: com.AdvancedDemos.FileAndIO.User; local class incompatible: stream classdesc serialVersionUID = -6815684908129422173, local class serialVersionUID = -1122598753892796139
         *
         * 序列化(写出)和反序列化(读入)serialVersionUID不一致,报错
         * 解决方案:手动提供不可变的serialVersionUID
         *
         * 一般写为 long类型常量:
         * private static final long serialVersionUID = 0L*/

        /**
         同时序列化(写出)以及反序列化(读入)多个对象  方案1:
         使用可以序列化的容器,存储可以序列化对象
         注意容器可以序列化,自定义类也必须要可以序列化
         */
        /*ArrayList<User> users = new ArrayList<>();
        users.add(new User("张三1", 23));
        users.add(new User("张三2", 23));
        users.add(new User("张三3", 23));
        users.add(new User("张三4", 23));
        //将集合序列化(写出)
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("helloworld\\src\\com\\AdvancedDemos\\FileAndIO\\objectStream.txt"));
        oos.writeObject(users);
        oos.close();*/

        //反序列化(读入)集合
        /*ObjectInputStream ois = new ObjectInputStream(new FileInputStream("helloworld\\src\\com\\AdvancedDemos\\FileAndIO\\objectStream.txt"));
        Object o = ois.readObject();
        System.out.println("----------------");
        System.out.println(o);
        System.out.println("----------------");
        if (o instanceof ArrayList) {
            users = (ArrayList<User>) o;//需要将Object强转成ArrayLIst<User>才能使用ArrayList的函数
        }
        ois.close();
        users.forEach(new Consumer<User>() {
            @Override
            public void accept(User user) {
                System.out.println(user);
                //User{name='张三1', age=23}
                //User{name='张三2', age=23}
                //User{name='张三3', age=23}
                //User{name='张三4', age=23}
            }
        });*/


        /**
         同时序列化(写出)以及反序列化(读入)多个对象  方案2:
         循环读,循环写
         */
        /*ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("helloworld\\src\\com\\AdvancedDemos\\FileAndIO\\objectStream.txt"));
        oos.writeObject(new User("张三1", 23));
        oos.writeObject(new User("张三2", 23));
        oos.writeObject(new User("张三3", 23));
        oos.writeObject(new User("张三4", 23));
        oos.writeObject(new User("张三5", 23));
        oos.close();*/

        //循环读取
        /*ObjectInputStream ois = new ObjectInputStream(new FileInputStream("helloworld\\src\\com\\AdvancedDemos\\FileAndIO\\objectStream.txt"));
        //readObject读文件读完碰到末尾时抛出异常EOFException
        ArrayList<User> o1;
        while (true) {
            try {
                Object o = ois.readObject();
                System.out.println(o);
                //User{name='张三1', age=23}
                //User{name='张三2', age=23}
                //User{name='张三3', age=23}
                //User{name='张三4', age=23}
                //User{name='张三5', age=23}
            } catch (EOFException e) {//读取到末尾会抛出EOFException
                break;
            }
        }
        ois.close();*/

        /**
         注意:不管文件中的内容是ArrayList<User>,单个User,还是多个User,objectInputStream.readObject()每次只会读取文件中的一个对象,它的返回值o都是Object类型,但o的实际类型取决于文件中的实际内容,也就是说如果之前是用集合封装好User写出去的,那么读的时候才可以(ArrayList<User>)o,如果写出去的时候是一个一个的User对象,那么读的时候每次objectInputStream.readObject()实际也只会读到一个User对象,其类型是Obejct,需要强转成User才可以调用User中特有的函数
         */


        //---------------------------------------------------

        //序列化过程中,对象中的一些信息,不想要进行序列化操作,只保留成员变量的声明
        //transient [ˈtrænʃnt] a.转瞬即逝的,短暂的
        //需要将不进行序列化的成员变量,使用关键字transient在类中对成员变量进行修饰,使得值丢失,置为类型的默认值,只保留声明
        // 如 private transient int age;//被修饰的成员变量值不会被序列化(写出)

        //User{name='张三1', age=0}
        //User{name='张三2', age=0}
        //User{name='张三3', age=0}
        //User{name='张三4', age=0}
        //User{name='张三5', age=0}

    }
}


class User implements Serializable {
    private static final long serialVersionUID = 0L;//用于解决序列化(写出)和反序列化(读入)serialVersionUID不一致报错问题
//    private String wtp;

    private String name;
    private transient int age;//被修饰的成员变量值不会被序列化(写出)


    public User() {
    }

    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }


    @Override
    public String toString() {
        return "User{" + "name='" + name + '\'' + ", age=" + age + '}';
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public void method() {
        System.out.println("成员方法method");
    }
}
